/*
 *  GeometricGestureClassifier.cpp
 *  
 *
 *  Created by Rasmus Kyng on 15/01/2011.
 *  Copyright 2011 __MyCompanyName__. All rights reserved.
 *
 */

#include "GestureClassifier.hpp"
#include "HandInterface.hpp"

using namespace cv;

GeometricGestureClassifier::GeometricGestureClassifier( Ptr< AbstractProbabilityTransformer > probTransPtr ) {
	
	m_ProbTransPtr = probTransPtr;	
	m_ConvexHullArea = 0;
	m_ContourArea = 0;
	m_RelativeConvexityDefect = 0;
	
}

GeometricGestureClassifier::GeometricGestureClassifier() {
	
	m_ProbTransPtr = Ptr< AbstractProbabilityTransformer > ( NULL );	
	m_ConvexHullArea = 0;
	m_ContourArea = 0;
	m_RelativeConvexityDefect = 0;
	
}

bool GeometricGestureClassifier::isHandActive( cv::Mat& handImg ) {
	return false; //Implemented only in subclass...
}

void GeometricGestureClassifier::getThresholdedBackProj( cv::Mat& handImg, cv::Mat& bpThreshed ) {
	Mat backProj, handHSV;
	
	//Compute HSV for backprojection
	cvtColor(handImg, handHSV, CV_BGR2HSV);	
	
	//Compute backprojection
	m_ProbTransPtr->getBackProjection( handHSV, backProj);
	
	threshold( backProj, bpThreshed, 20, 255, THRESH_BINARY); //FIX HOW to pick threshold? //FULLTEST
}

bool GeometricGestureClassifier::drawBPContours( cv::Mat& bpThreshed ) {
	//Compute contours
	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;
	
	findContours( bpThreshed, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE );
	
	//get largest contour:
	int contourCount = contours.size();
	int largestContourIndex = -1;
	int secondLargestContourIndex = -1;
	m_ContourArea = 0;
	
	if ( contourCount > 0 ) {
		for( int i = 0; i<contourCount; i++) {
			double currentContourArea = contourArea( Mat( contours[i] ) );
			if ( currentContourArea > m_ContourArea ) {
				m_ContourArea = currentContourArea;
				secondLargestContourIndex = largestContourIndex;
				largestContourIndex = i;
			}
		}
		
		
		double contourAreaThreshold = bpThreshed.cols*bpThreshed.rows / 16;//FULLTEST /20?
		
		if (m_ContourArea < contourAreaThreshold) {
			return false;
		}
		
		//Compute convex hull
		vector<Point> convexHullVector;
		convexHull( Mat( contours[largestContourIndex] ), convexHullVector );
		
		//Compute relative convexity defect and related areas
		m_ConvexHullArea = contourArea( Mat(convexHullVector) );
		m_RelativeConvexityDefect = ( m_ConvexHullArea - m_ContourArea ) / m_ConvexHullArea;
		
		//Compute contour as mask
		m_Contour =  Mat::zeros( bpThreshed.size(), CV_8UC1); //FIX optimise?
		//fillConvexPoly(m_Contour, &contours[largestContourIndex][0], contours[largestContourIndex].size(), Scalar::all(255) );
		
		int contourIndex = largestContourIndex;
		const int maxLevel = 0; //draw only this contour
		const int lineType = 8; //see docs
		
		drawContours( m_Contour, contours, contourIndex, Scalar::all(255), CV_FILLED, lineType, hierarchy, maxLevel );
		drawContours( m_Contour, contours, secondLargestContourIndex, Scalar::all(255), CV_FILLED, lineType, hierarchy, maxLevel ); //FIXNOW draw all larger contours?
		
		return true;
	} else {
		return false;
	}
}

bool GeometricGestureClassifier::segment( cv::Mat& handImg ) {
	//threshold backprojection
	Mat bpThreshed; //NOTE is consumed by findContours
	getThresholdedBackProj( handImg, bpThreshed );
	//return drawBPContours( bpThreshed );
	bool drawn = drawBPContours( bpThreshed );
//	
//	HiVisualiser::windowMatrixMap["primary"+(long)this] = m_Contour;
//	HiVisualiser::refreshWindow("primary"+(long)this);
//	
	return drawn;
}

bool GeometricGestureClassifier::segment( cv::Mat& handImg, cv::Rect& forbiddenRect ) {
	//threshold backprojection
	Mat bpThreshed; //NOTE is consumed by findContours
	getThresholdedBackProj( handImg, bpThreshed );
	
	Mat forbiddenRegion( bpThreshed, forbiddenRect );
	
	forbiddenRegion = Mat::zeros(forbiddenRect.size(), CV_8UC1 );
	
	if (!drawBPContours( bpThreshed )) {
		m_Contour =  Mat::zeros( bpThreshed.size(), CV_8UC1 );
		Mat forbiddenRegion( m_Contour, forbiddenRect );
		forbiddenRegion = Mat::zeros( forbiddenRect.size(), CV_8UC1 );
	}
	
	HiVisualiser::windowMatrixMap["intersectedBP"] = m_Contour;
	HiVisualiser::refreshWindow("intersectedBP");
	return true;
}

void GeometricGestureClassifier::updateHist( const cv::Mat& handImg, const cv::Mat& mask ) {
	Mat handHSV;
	//Compute HSV for backprojection
	cvtColor(handImg, handHSV, CV_BGR2HSV);
	m_ProbTransPtr->updateHist( handHSV, mask );
}

//DEPRECATED?
bool GeometricGestureClassifier::classify( Mat& handImg ) {
	return segment( handImg );
}


double GeometricGestureClassifier::getRelativeConvexityDefect() { return m_RelativeConvexityDefect; }
double GeometricGestureClassifier::getContourArea() { return m_ContourArea; }
double GeometricGestureClassifier::getConvexHullArea() { return m_ConvexHullArea; }
Mat& GeometricGestureClassifier::getContourImage() { return m_Contour; }
